home *** CD-ROM | disk | FTP | other *** search
/ Aminet 40 / Aminet 40 (2000)(Schatztruhe)[!][Dec 2000].iso / Aminet / util / sys / NewSearch.lha / NewSearch / source / Search.c < prev   
C/C++ Source or Header  |  2000-10-21  |  14KB  |  556 lines

  1. /*
  2.  
  3. File: Search.c
  4. Author: Neil Cafferkey
  5. Placed in the public domain by Neil Cafferkey.
  6.  
  7. */
  8.  
  9.  
  10. #include <proto/exec.h>
  11. #include <proto/dos.h>
  12. #include <proto/locale.h>
  13. #include <exec/memory.h>
  14. #include <dos/dos.h>
  15.  
  16.  
  17. typedef ULONG UPINT;
  18.  
  19. #define DOS_VERSION 37
  20. #define LOCALE_VERSION 38
  21. #define PATH_BUF_SIZE 512
  22. #define SPACES_SIZE 160+1
  23. #define MARGIN 3
  24. #define INDENT 5
  25. #define DIR_MARGIN 2
  26.  
  27. struct ExecBase *SysBase;
  28. struct DosLibrary *DOSBase;
  29. struct LocaleBase *LocaleBase;
  30.  
  31.  
  32. enum
  33. {
  34.    ARG_FROM,
  35.    ARG_SEARCH,
  36.    ARG_ALL,
  37.    ARG_NONUM,
  38.    ARG_QUIET,
  39.    ARG_QUICK,
  40.    ARG_FILE,
  41.    ARG_PATTERN,
  42.    ARG_COUNT
  43. };
  44.  
  45.  
  46. VOID PrintFullName(TEXT *buffer,UWORD cut_off,struct AnchorPath *anchor);
  47. UWORD GetDirName(struct AnchorPath *anchor,TEXT *buffer);
  48. BOOL FindString(struct AnchorPath *anchor,UPINT *args,TEXT *pattern,
  49.    struct Locale *locale,UBYTE *pi);
  50. BOOL MatchStringNoCase(TEXT *string,TEXT *text,TEXT *text_end,UBYTE *pi,
  51.    struct Locale *locale);
  52. ULONG strlen(STRPTR s);
  53.  
  54. extern const TEXT template[];
  55. extern const TEXT dos_name[];
  56. extern const TEXT locale_name[];
  57. extern const TEXT control_codes[];
  58. extern const TEXT line_show[];
  59. extern const TEXT wild_card[];
  60. extern const TEXT new_line[];
  61. extern const TEXT abandon_msg[];
  62.  
  63.  
  64. ULONG Search(VOID)
  65. {
  66.    UPINT *args;
  67.    struct RDArgs *read_args;
  68.    struct DosLibrary *dos_base;
  69.    struct LocaleBase *locale_base;
  70.    struct AnchorPath *anchor;
  71.    LONG error,return_code=RETURN_WARN;
  72.    TEXT *text,*spaces,*pattern=NULL,*path_buffer,*user_pattern=NULL,*p,ch,
  73.       **from;
  74.    BOOL found,success=TRUE,new_dir,print_names;
  75.    UWORD indent=0,pat_buf_length,cut_off,pat_length;
  76.    UBYTE k,q;
  77.    struct Locale *locale;
  78.  
  79.    /* Open libraries */
  80.  
  81.    SysBase=*((APTR *)sizeof(APTR));
  82.    dos_base=(struct DosLibrary *)OpenLibrary(dos_name,DOS_VERSION);
  83.    if(!dos_base)
  84.       return RETURN_FAIL;
  85.    DOSBase=dos_base;
  86.    locale_base=(struct LocaleBase *)OpenLibrary(locale_name,LOCALE_VERSION);
  87.  
  88.    /* Allocate buffers */
  89.  
  90.    spaces=AllocMem(SPACES_SIZE,MEMF_CLEAR);
  91.    anchor=AllocMem(sizeof(struct AnchorPath),MEMF_CLEAR);
  92.    args=AllocMem(ARG_COUNT*sizeof(APTR),MEMF_CLEAR);
  93.    path_buffer=AllocMem(PATH_BUF_SIZE,MEMF_ANY);
  94.  
  95.    if(args&&anchor&&locale_base&&spaces&&path_buffer)
  96.    {
  97.       LocaleBase=locale_base;
  98.       locale=OpenLocale(NULL);
  99.  
  100.       for(text=spaces+SPACES_SIZE-1;text>spaces;*(--text)=' ');
  101.  
  102.       /* Parse arguments */
  103.  
  104.       read_args=ReadArgs(template,args,NULL);
  105.       if(locale&&read_args)
  106.       {
  107.          /* Prepare the pattern to be matched */
  108.  
  109.          pat_length=strlen((TEXT *)args[ARG_SEARCH]);
  110.          pat_buf_length=pat_length*2+3;
  111.          user_pattern=AllocMem(pat_length+5,MEMF_CLEAR);
  112.          pattern=AllocMem(pat_buf_length,MEMF_ANY);
  113.          if(user_pattern&&pattern)
  114.          {
  115.             if(args[ARG_PATTERN]||args[ARG_FILE])
  116.             {
  117.                if(args[ARG_FILE])
  118.                   text=user_pattern;
  119.                else
  120.                {
  121.                   text=user_pattern+2;
  122.                   CopyMem(wild_card,user_pattern,2);
  123.                   CopyMem(wild_card,text+pat_length,2);
  124.                }
  125.  
  126.                CopyMem((TEXT *)args[ARG_SEARCH],text,pat_length);
  127.  
  128.                if(ParsePatternNoCase(user_pattern,pattern,pat_buf_length)<0)
  129.                   success=FALSE;
  130.             }
  131.             else
  132.             {
  133.                /* Copy the search string and convert it to uppercase */
  134.  
  135.                text=pattern;
  136.                for(p=(TEXT *)args[ARG_SEARCH];(ch=*p)!='\0';p++)
  137.                   *(text++)=ConvToUpper(locale,ch);
  138.                *text='\0';
  139.  
  140.                /* Construct prefix table for Knuth-Morris-Pratt algorithm */
  141.  
  142.                *user_pattern=0;
  143.                k=0;
  144.                for(q=1;q<pat_length;q++)
  145.                {
  146.                   while(k&&(pattern[k]!=pattern[q]))
  147.                      k=user_pattern[k-1];
  148.                   if(pattern[k]==pattern[q])
  149.                      k++;
  150.                   user_pattern[q]=k;
  151.                }
  152.             }
  153.          }
  154.          else
  155.             success=FALSE;
  156.  
  157.          /* Get the next starting point */
  158.  
  159.          for(from=*(((TEXT ***)args)+ARG_FROM);*from&&success;from++)
  160.          {
  161.  
  162.             /* Initialise file search */
  163.  
  164.             anchor->ap_BreakBits=SIGBREAKF_CTRL_C;
  165.             anchor->ap_FoundBreak=0;
  166.             anchor->ap_Flags=0;
  167.             error=MatchFirst(*from,anchor);
  168.  
  169.             /* Work out if more than one file is being searched */
  170.  
  171.             print_names=(*(*(((TEXT ***)args)+ARG_FROM)+1))
  172.                ||(anchor->ap_Flags&APF_ITSWILD);
  173.  
  174.             /* Enter sub-dir if the pattern was an explicitly named dir */
  175.  
  176.             if(!(anchor->ap_Flags&APF_ITSWILD)
  177.                &&(anchor->ap_Info.fib_DirEntryType>0))
  178.                anchor->ap_Flags|=APF_DODIR;
  179.  
  180.             /* Set flag to get name of starting directory */
  181.  
  182.             new_dir=TRUE;
  183.  
  184.             /* Traverse the directory */
  185.  
  186.             while(!error&&success)
  187.             {
  188.                found=FALSE;
  189.  
  190.                if(anchor->ap_Info.fib_DirEntryType>0)
  191.                {
  192.                   /* Enter sub-dir if the ALL switch was supplied and we're
  193.                      not on the way out of it */
  194.  
  195.                   if(!(anchor->ap_Flags&APF_DIDDIR))
  196.                   {
  197.                      if(!(args[ARG_FILE]||args[ARG_QUIET]||args[ARG_QUICK]))
  198.                      {
  199.                         WriteChars(spaces,MARGIN+INDENT*indent+DIR_MARGIN);
  200.                         text=(TEXT *)&(anchor->ap_Info.fib_FileName);
  201.                         VPrintf("%s (dir)\n",&text);
  202.                      }
  203.                      if(args[ARG_ALL]||(anchor->ap_Flags&APF_DODIR))
  204.                      {
  205.                         anchor->ap_Flags|=APF_DODIR;
  206.                         indent++;
  207.                         print_names=TRUE;
  208.                      }
  209.                   }
  210.                   else
  211.                   {
  212.                      indent--;
  213.                   }
  214.  
  215.                   new_dir=TRUE;
  216.                   anchor->ap_Flags&=~APF_DIDDIR;
  217.                }
  218.                else
  219.                {
  220.                   /* Deal with a file */
  221.  
  222.                   if(anchor->ap_Flags&APF_DirChanged)
  223.                      new_dir=TRUE;
  224.  
  225.                   if(new_dir)
  226.                   {
  227.                      if(!(cut_off=GetDirName(anchor,path_buffer)))
  228.                         success=FALSE;
  229.                      new_dir=FALSE;
  230.                   }
  231.  
  232.                   if(args[ARG_FILE])
  233.                   {
  234.                      found=MatchPatternNoCase(pattern,
  235.                         (TEXT *)&(anchor->ap_Info.fib_FileName));
  236.                   }
  237.                   else
  238.                   {
  239.                      if(args[ARG_QUICK])
  240.                      {
  241.                         PrintFullName(path_buffer,cut_off,anchor);
  242.                         WriteChars(control_codes,3);
  243.                      }
  244.                      else if(!args[ARG_QUIET]&&print_names)
  245.                      {
  246.                         WriteChars(spaces,MARGIN+INDENT*indent);
  247.                         text=(TEXT *)&(anchor->ap_Info.fib_FileName);
  248.                         VPrintf("%s..\n",&text);
  249.                      }
  250.  
  251.                      found=FindString(anchor,args,pattern,locale,
  252.                         user_pattern);
  253.  
  254.                   }
  255.  
  256.                   if(found)
  257.                   {
  258.                      if(args[ARG_FILE]||args[ARG_QUIET]&&!args[ARG_QUICK])
  259.                      {
  260.                         PrintFullName(path_buffer,cut_off,anchor);
  261.                         PutStr(new_line);
  262.                      }
  263.                      return_code=RETURN_OK;
  264.                   }
  265.                }
  266.  
  267.                error=MatchNext(anchor);
  268.                if(error&&(error!=ERROR_NO_MORE_ENTRIES))
  269.                {
  270.                   success=FALSE;
  271.                   SetIoErr(error);
  272.                }
  273.  
  274.             }
  275.          }
  276.  
  277.          /* Clear line for next shell prompt */
  278.  
  279.          if(args[ARG_QUICK]&&!args[ARG_FILE])
  280.             WriteChars(control_codes,2);
  281.  
  282.          MatchEnd(anchor);
  283.  
  284.          if(success)
  285.             SetIoErr(0);
  286.       }
  287.  
  288.       FreeArgs(read_args);
  289.  
  290.       CloseLocale(locale);
  291.    }
  292.    else
  293.    {
  294.       SetIoErr(ERROR_NO_FREE_STORE);
  295.    }
  296.  
  297.    /* Free memory */
  298.  
  299.    if(args)
  300.       FreeMem(args,ARG_COUNT*sizeof(APTR));
  301.    if(anchor)
  302.       FreeMem(anchor,sizeof(struct AnchorPath));
  303.    if(spaces)
  304.       FreeMem(spaces,SPACES_SIZE);
  305.    if(path_buffer)
  306.       FreeMem(path_buffer,PATH_BUF_SIZE);
  307.    if(user_pattern)
  308.       FreeMem(user_pattern,pat_length+5);
  309.    if(pattern)
  310.       FreeMem(pattern,pat_buf_length);
  311.  
  312.    /* Close libraries and exit */
  313.  
  314.    if(locale_base)
  315.       CloseLibrary((struct Library *)LocaleBase);
  316.    CloseLibrary((struct Library *)DOSBase);
  317.  
  318.    /* Check and reset signals */
  319.  
  320.    if(SetSignal(0,-1)&SIGBREAKF_CTRL_C)
  321.       SetIoErr(ERROR_BREAK);
  322.  
  323.    /* Exit */
  324.  
  325.    if(error=IoErr())
  326.    {
  327.       PrintFault(error,NULL);
  328.       return RETURN_FAIL;
  329.    }
  330.  
  331.    return return_code;
  332. }
  333.  
  334.  
  335.  
  336. VOID PrintFullName(TEXT *buffer,UWORD cut_off,struct AnchorPath *anchor)
  337. {
  338.    buffer[cut_off]='\0';
  339.    if(AddPart(buffer,(TEXT *)&(anchor->ap_Info.fib_FileName),PATH_BUF_SIZE))
  340.    {
  341.       PutStr(buffer);
  342.    }
  343.  
  344.    return;
  345. }
  346.  
  347.  
  348.  
  349. UWORD GetDirName(struct AnchorPath *anchor,TEXT *buffer)
  350. {
  351.    if(NameFromLock(anchor->ap_Current->an_Lock,buffer,
  352.       PATH_BUF_SIZE))
  353.       return strlen(buffer);
  354.    return 0;
  355. }
  356.  
  357.  
  358.  
  359. BOOL FindString(struct AnchorPath *anchor,UPINT *args,TEXT *pattern,
  360.    struct Locale *locale,UBYTE *pi)
  361. {
  362.    BOOL found=FALSE,end_early=FALSE,line_matches,at_end;
  363.    BPTR old_lock,file;
  364.    TEXT *p,*q,*r,*line,*buffer=NULL,ch;
  365.    UPINT max_line_length=0,line_length,offset=0,file_size,buf_size,
  366.       line_start=0,line_count=1;
  367.    ULONG sigs;
  368.    LONG read_length=1;
  369.  
  370.    /* Move into the file's directory */
  371.  
  372.    old_lock=CurrentDir(anchor->ap_Current->an_Lock);
  373.  
  374.    /* Open the file for reading */
  375.  
  376.    if(file=Open((TEXT *)&(anchor->ap_Info.fib_FileName),MODE_OLDFILE))
  377.    {
  378.       /* Get a buffer for the file */
  379.  
  380.       file_size=anchor->ap_Info.fib_Size;
  381.       buf_size=file_size+1;
  382.  
  383.       while(!buffer&&buf_size)
  384.       {
  385.          if(!(buffer=AllocMem(buf_size,MEMF_ANY)))
  386.             buf_size>>=1;
  387.       }
  388.  
  389.       /* Check size of buffer */
  390.  
  391.       if((buf_size<=file_size)&&buffer)
  392.       {
  393.          /* Get length of longest line */
  394.  
  395.          while(read_length>0)
  396.          {
  397.             read_length=Read(file,buffer,buf_size-1);
  398.             q=buffer+read_length;
  399.             if(!read_length)
  400.                q++;
  401.             for(p=buffer;p<q;p++)
  402.                if((*p=='\n')||!read_length)
  403.                {
  404.                   line_length=offset+(p-buffer)-line_start;
  405.                   if(line_length>max_line_length)
  406.                      max_line_length=line_length;
  407.                   line_start=offset+(p-buffer)+1;
  408.                }
  409.             offset+=read_length;
  410.          }
  411.  
  412.          /* Ensure buffer is big enough for longest line */
  413.  
  414.          if(buf_size<=max_line_length)
  415.          {
  416.             buf_size=max_line_length+1;
  417.             buffer=AllocMem(buf_size,MEMF_ANY);
  418.          }
  419.       }
  420.  
  421.       /* Test every line against the pattern */
  422.  
  423.       if(buffer&&pattern)
  424.       {
  425.  
  426.          read_length=Seek(file,0,OFFSET_BEGINNING)+1;
  427.          while(((read_length=Read(file,buffer,buf_size-1))>0)&&!end_early)
  428.          {
  429.             q=buffer+read_length;
  430.             at_end=Seek(file,0,OFFSET_CURRENT)==file_size;
  431.             if(at_end)
  432.                *(q++)='\0';
  433.             line=buffer;
  434.             for(p=buffer;(p<q)&&!end_early;p++)
  435.             {
  436.                ch=*p;
  437.                if((ch=='\n')||(ch=='\0'))
  438.                {
  439.                   *p='\0';
  440.  
  441.                   if(args[ARG_PATTERN])
  442.                      line_matches=MatchPatternNoCase(pattern,line);
  443.                   else
  444.                      line_matches=
  445.                         MatchStringNoCase(pattern,line,p,pi,locale);
  446.  
  447.                   if(line_matches)
  448.                   {
  449.  
  450.                      if(!found&&args[ARG_QUICK])
  451.                         PutStr(new_line);
  452.  
  453.                      found=TRUE;
  454.  
  455.                      if(args[ARG_QUIET])
  456.                      {
  457.                         end_early=TRUE;
  458.                      }
  459.                      else
  460.                      {
  461.                         if(!args[ARG_NONUM])
  462.                            VPrintf("%6lu ",&line_count);
  463.  
  464.                         /* Replace invisible characters with dots */
  465.  
  466.                         for(r=line;r<p;r++)
  467.                            if(!IsPrint(locale,*r))
  468.                               *r='.';
  469.  
  470.                         VPrintf("%s\n",&line);
  471.                      }
  472.                   }
  473.                   line=p+1;
  474.                   if(ch=='\n')
  475.                      line_count++;
  476.  
  477.                   sigs=SetSignal(0,SIGBREAKF_CTRL_D);
  478.                   if(sigs&(SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D))
  479.                   {
  480.                      end_early=TRUE;
  481.                      if(sigs&SIGBREAKF_CTRL_D)
  482.                      {
  483.                         PutStr(abandon_msg);
  484.                      }
  485.                   }
  486.                }
  487.             }
  488.  
  489.             /* Start reading again at start of most recent line */
  490.  
  491.             if(!at_end)
  492.                Seek(file,line-q,OFFSET_CURRENT);
  493.          }
  494.  
  495.       }
  496.  
  497.       if(buffer)
  498.          FreeMem(buffer,buf_size);
  499.  
  500.       Close(file);
  501.    }
  502.  
  503.    CurrentDir(old_lock);
  504.  
  505.    return found;
  506. }
  507.  
  508.  
  509.  
  510. const TEXT template[]=
  511.    "FROM/M,SEARCH/A,ALL/S,NONUM/S,QUIET/S,QUICK/S,FILE/S,PATTERN/S";
  512. const TEXT version_string[]="$VER: Search 41.2 (14.10.2000)";
  513. const TEXT dos_name[]=DOSNAME;
  514. const TEXT locale_name[]="locale.library";
  515.  
  516. const TEXT control_codes[]={0x9b,'K',13};
  517. const TEXT wild_card[]={'#','?'};
  518. const TEXT new_line[]="\n";
  519. const TEXT abandon_msg[]="** File abandoned\n";
  520.  
  521.  
  522.  
  523. BOOL MatchStringNoCase(TEXT *string,TEXT *text,TEXT *text_end,UBYTE *pi,
  524.    struct Locale *locale)
  525. {
  526.    TEXT *s,ch;
  527.  
  528.    s=string;
  529.    while(text<text_end)
  530.    {
  531.       ch=ConvToUpper(locale,*(text++));
  532.       while((s!=string)&&(*s!=ch))
  533.          s=string+pi[s-string-1];
  534.       if(ch==*s)
  535.          s++;
  536.  
  537.       if(!*s)
  538.          return TRUE;
  539.    }
  540.    return FALSE;
  541. }
  542.  
  543.  
  544.  
  545. ULONG strlen(STRPTR s)
  546. {
  547.    STRPTR p;
  548.  
  549.    p=s;
  550.    for(p=s;*p!='\0';p++);
  551.    return p-s;
  552. }
  553.  
  554.  
  555.  
  556.